package pl;

import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.RSAPrivateKey;
import javacard.security.RSAPublicKey;

public class RsaKeyParser {

	/* public key tags */
	static byte MODULUS_N_TAG = (byte) 0x81;
	static byte PEXPONENT_PUBLIC_KEY_TAG = (byte) 0x82;

	/* private key tags */
	static byte PRIME_P_TAG = (byte) 0x92;
	static byte PRIME_Q_TAG = (byte) 0x93;
	static byte PRIME_IQ_TAG = (byte) 0x94;
	static byte PRIME_DP_TAG = (byte) 0x95;
	static byte PRIME_DQ_TAG = (byte) 0x96;
	static byte PRIVATE_KEY_EXPONENT_D_TAG = (byte) 0x97;

	static byte[] PRIVATE_KEY_DO_TAG = new byte[] { 0x7F, 0x48 };
	static byte[] PUBLIC_KEY_DO_TAG = new byte[] { 0x7F, 0x49 };
	static byte[] KEY_LENGTH = new byte[] { (byte) 0xDF, 0x25 };

	/* tlv structure tags */
	static byte HEADER_TAG = (byte) 0x4D;
	static byte PUBLIC_KEY_ID_TAG = (byte) 0x83;
	static byte PRIVATE_KEY_ID_TAG = (byte) 0x84;
	static byte KEY_ID_MAIN_TAG = (byte) 0xB6;

	/* key import tags */
	static ConstructedBERTag PRIVATE_KEY_DO;
	static ConstructedBERTag PUBLIC_KEY_DO;
	static ConstructedBERTag KEY_ID_MAIN;

	static PrimitiveBERTag MODULUS;
	static PrimitiveBERTag PRIVATE_EXPONENT;
	static PrimitiveBERTag PUBLIC_EXPONENT;
	static PrimitiveBERTag PRIVATE_KEY_ID;
	static PrimitiveBERTag PUBLIC_KEY_ID;

	/* key tlv */
	ConstructedBERTLV input;
	ConstructedBERTLV privateKeyContentTlv;
	static ConstructedBERTLV publicKeyContentTlv;

	PrimitiveBERTLV exponentPrivateTlv;
	static PrimitiveBERTLV modulusTlv;
	static PrimitiveBERTLV publicExponentTlv;

	static PrimitiveBERTLV keyIdTlv;

	byte[] privateKeyExponent;
	byte[] publicKeyExponent;
	byte[] modulus;

	short privateKeyExponentLength;
	short publicKeyExponentLength;
	short modulusLength;

	boolean initiliazed;
	short keySize;

	public RsaKeyParser() {
		privateKeyExponent = JCSystem.makeTransientByteArray((short) 256,
				JCSystem.CLEAR_ON_DESELECT);
		modulus = JCSystem.makeTransientByteArray((short) 256,
				JCSystem.CLEAR_ON_DESELECT);
		publicKeyExponent = JCSystem.makeTransientByteArray((short) 3,
				JCSystem.CLEAR_ON_DESELECT);
		initTagsAndTlvs();
	}

	private void initTagsAndTlvs() {
		/* constructed tags */
		PRIVATE_KEY_DO = (ConstructedBERTag) BERTag.getInstance(
				PRIVATE_KEY_DO_TAG, (short) 0);
		PUBLIC_KEY_DO = (ConstructedBERTag) BERTag.getInstance(
				PUBLIC_KEY_DO_TAG, (short) 0);
		KEY_ID_MAIN = (ConstructedBERTag) BERTag.getInstance(
				new byte[] { KEY_ID_MAIN_TAG }, (short) 0);

		/* primitive tags */
		PRIVATE_KEY_ID = (PrimitiveBERTag) BERTag.getInstance(
				new byte[] { PRIVATE_KEY_ID_TAG }, (short) 0);
		PUBLIC_KEY_ID = (PrimitiveBERTag) BERTag.getInstance(
				new byte[] { PUBLIC_KEY_ID_TAG }, (short) 0);

		MODULUS = (PrimitiveBERTag) BERTag.getInstance(
				new byte[] { MODULUS_N_TAG }, (short) 0);
		PRIVATE_EXPONENT = (PrimitiveBERTag) BERTag.getInstance(
				new byte[] { PRIVATE_KEY_EXPONENT_D_TAG }, (short) 0);
		PUBLIC_EXPONENT = (PrimitiveBERTag) BERTag.getInstance(
				new byte[] { PEXPONENT_PUBLIC_KEY_TAG }, (short) 0);

		/* constructedBERTLV */
		input = new ConstructedBERTLV((short) 6);
		privateKeyContentTlv = new ConstructedBERTLV((short) 6);
		publicKeyContentTlv = new ConstructedBERTLV((short) 2); // modulus +
																// public
																// exponent
		/* primitiveBERTLV */
		keyIdTlv = new PrimitiveBERTLV((short) 1);
	}

	public void initalize(byte[] inputArray, short offset, short len,
			RSAPrivateKey privateKey, RSAPublicKey publicKey) {

		input.init(inputArray, offset, len);

		/* detect private key parameters */
		privateKeyContentTlv = (ConstructedBERTLV) input.find(PRIVATE_KEY_DO);
		if (privateKeyContentTlv != null) {
			/* private exponent */
			exponentPrivateTlv = (PrimitiveBERTLV) privateKeyContentTlv
					.find(PRIVATE_EXPONENT);
			if (exponentPrivateTlv != null) {
				privateKeyExponentLength = (short) exponentPrivateTlv.theValue.length;

				Util
						.arrayCopy(exponentPrivateTlv.theValue, (short) 0,
								privateKeyExponent, (short) 0,
								privateKeyExponentLength);
			}
		}

		/* detect public key parameters */
		publicKeyContentTlv = (ConstructedBERTLV) input.find(PUBLIC_KEY_DO);
		if (publicKeyContentTlv != null) {
			/* modulus */
			modulusTlv = (PrimitiveBERTLV) publicKeyContentTlv.find(MODULUS);
			if (modulusTlv != null) {
				modulusLength = (short) modulusTlv.theValue.length;

				Util.arrayCopy(modulusTlv.theValue, (short) 0, modulus,
						(short) 0, modulusLength);
			}

			/* public exponent */
			publicExponentTlv = (PrimitiveBERTLV) publicKeyContentTlv
					.find(PUBLIC_EXPONENT);
			if (publicExponentTlv != null) {
				publicKeyExponentLength = (short) publicExponentTlv.theValue.length;

				Util.arrayCopy(publicExponentTlv.theValue, (short) 0,
						publicKeyExponent, (short) 0, publicKeyExponentLength);

			}
		}
		if (modulusLength != 0 && publicKeyExponentLength != 0
				&& privateKeyExponentLength != 0) {
			if (modulusLength == privateKeyExponentLength) {
				this.initiliazed = true;
				this.keySize = (short) (modulusLength*8);
			}
		}
	}

	public RSAPrivateKey getInstancePrivateKey(byte[] inputArray) {
		input = new ConstructedBERTLV((short) 3);
		input.init(inputArray, (short) 0, (short) inputArray.length);

		/* private key */
		keyIdTlv = (PrimitiveBERTLV) input.find(KEY_ID_MAIN);
		if (keyIdTlv == null)
			return null;

		privateKeyContentTlv = (ConstructedBERTLV) input.find(PRIVATE_KEY_DO);
		if (privateKeyContentTlv == null)
			return null;

		exponentPrivateTlv = (PrimitiveBERTLV) privateKeyContentTlv
				.find(PRIVATE_EXPONENT);
		if (exponentPrivateTlv == null)
			return null;

		modulusTlv = (PrimitiveBERTLV) privateKeyContentTlv
				.find(PRIVATE_EXPONENT);
		if (modulusTlv == null)
			return null;

		return null;
	}

	public RSAPrivateKey getInstancePublicKey(byte[] inputArray) {
		// expected 3 TLV's -> key id, modulus, public exponent
		input = new ConstructedBERTLV((short) 3);
		input.init(inputArray, (short) 0, (short) inputArray.length);

		/* public key tags */
		/*
		 * static byte MODULUS_N_TAG = (byte)0x81; static byte
		 * PEXPONENT_PUBLIC_KEY_TAG = (byte)0x82;
		 */

		/* public */
		keyIdTlv = (PrimitiveBERTLV) input.find(KEY_ID_MAIN);
		if (keyIdTlv == null)
			return null;

		privateKeyContentTlv = (ConstructedBERTLV) input.find(PRIVATE_KEY_DO);
		if (privateKeyContentTlv == null)
			return null;

		exponentPrivateTlv = (PrimitiveBERTLV) privateKeyContentTlv
				.find(PRIVATE_EXPONENT);
		if (exponentPrivateTlv == null)
			return null;

		modulusTlv = (PrimitiveBERTLV) privateKeyContentTlv
				.find(PRIVATE_EXPONENT);
		if (modulusTlv == null)
			return null;

		return null;
	}

}
